WebGPU API 提案

2017 年 1 月 30 日 - dino@apple.com

目录

  1. 引言
  2. 待办事项
  3. API
  4. 着色语言
  5. 示例
  6. 更新日志

1. 引言

这是 Apple 关于 WebGPU API 的草案提案。它最初是对 Metal 到 JavaScript 的映射,但这不会是它的最终形态。Metal 中不仅有一些内容与 Vulkan 和 D3D12 不完全契合,我们也不希望被 Metal API 绑定。因此,请将其视为一项正在进行的工作。文末有一个小的源代码示例(尽管有点过时)。

与传统的原生 GPU API 的主要区别在于

2. 待办事项

3. API


interface WebGPURenderingContext : CanvasRenderingContext {

    // -- Identification and feature detection
    
    readonly attribute DOMString name;
    boolean supportsFeatureSet(WebGPUFeatureSet featureSet);

    // -- Library creation

    WebGPULibrary createLibrary(DOMString sourceCode);

    // -- Command queue creation
    
    WebGPUCommandQueue createCommandQueue();
    // @@ Do we need createCommandQueueWithMaxCommandBufferCount?

    // -- Resources

    WebGPUBuffer createBuffer(ArrayBufferView data);
    WebGPUTexture createTexture(WebGPUTextureDescriptor descriptor);
    WebGPUSamplerState createSamplerState(WebGPUSamplerDescriptor descriptor);

    // -- Rendering objects
    WebGPUDepthStencilState createDepthStencilState(WebGPUDepthStencilDescriptor descriptor);
    WebGPURenderPipelineState createRenderPipelineState(WebGPURenderPipelineDescriptor
                                                          descriptor);
    // @@ Do we need the completion block versions?

    // -- Compute
    WebGPUComputePipelineState createComputePipelineState(WebGPUFunction function);
    WebGPUComputePipelineState createComputePipelineState(WebGPUComputePipelineDescriptor
                                                            descriptor);

    // -- Getting the rendering destination
    WebGPUDrawable nextDrawable();
    // @@ Come up with a better way to do this.

};

interface WebGPUCommandQueue {

    attribute DOMString label;

    WebGPUCommandBuffer createCommandBuffer();
};

interface WebGPUCommandBuffer {

    // -- Command Encoders
    WebGPURenderCommandEncoder createRenderCommandEncoder(WebGPURenderPassDescriptor
                                                            descriptor);
    WebGPUBlitCommandEncoder createBlitCommandEncoder();
    WebGPUComputeCommandEncoder createComputeCommandEncoder();

    // -- Status
    readonly attribute WebGPUStatus status;
    readonly attribute DOMString error; // @@ need enum

    // - Execution

    void commit();
    void presentDrawable(WebGPUDrawable drawable);
    readonly attribute Promise scheduled;
    readonly attribute Promise completed;
    // @@ Should the completed promise be the return value of commit()?

    // @@ Maybe add enqueue or invent some way to offload filling the buffer
    // to workers?
};

interface WebGPUDrawable {

    readonly attribute WebGPUTexture texture; // @@ Only the framebuffer should have this.

    void present();

};

interface WebGPUCommandEncoder {

    readonly attribute DOMString label;

    void endEncoding();
    
    // @@ Debugging helpers?
}

interface WebGPURenderCommandEncoder : WebGPUCommandEncoder {

    // -- State

    // @@ Should these be attributes?
    void setBlendColor(float red, float green, float blue, float alpha);
    void setCullMode(WebGPUCullMode mode);
    void setDepthBias(float bias, float scale, float clamp);
    void setDepthClipMode(WebGPUDepthClipMode mode);
    void setDepthStencilState(WebGPUDepthStencilState depthStencilState);
    void setFrontFacingWinding(WebGPUWinding mode);
    void setRenderPipelineState(WebGPURenderPipelineState pipelineState);
    // @@ Use Geometry interfaces?
    void setScissorRect(float x, float y, float width, float height);
    void setStencilReferenceValue(unsigned long value);
    // @@ Check if we can overload here
    void setStencilReferenceValue(unsigned long front, unsigned long back);
    void setTriangleFillMode(WebGPUTriangleFill mode);
    void setViewport(WebGPUViewportDictionary viewport);
    void setVisibilityResultMode(WebGPUVisibilityResultMode mode, unsigned long offset);

    // -- Resources
    
    void setVertexBuffer(WebGPUBuffer buffer, unsigned long offset, unsigned long index);
    void setVertexBuffers(WebGPUBuffer[] buffers, unsigned long[] offsets,
                          unsigned long startIndex, unsigned long count);
    // @@ need setVertexBytes?
    void setVertexSamplerState(WebGPUSamplerState samplerState, unsigned long index);
    void setVertexSamplerStates(WebGPUSamplerState[] samplerState, unsigned long startIndex,
                                unsigned long count);
    void setVertexTexture(WebGPUTexture texture, unsigned long index);
    void setVertexTextures(WebGPUTexture[] textures, unsigned long startIndex,
                           unsigned long count);

    void setFragmentBuffer(WebGPUBuffer buffer, unsigned long offset, unsigned long index);
    void setFragmentBuffers(WebGPUBuffer[] buffers, unsigned long[] offsets,
                            unsigned long startIndex, unsigned long count);
    // @@ need setFragmentBytes?
    void setFragmentSamplerState(WebGPUSamplerState samplerState, unsigned long index);
    void setFragmentSamplerStates(WebGPUSamplerState[] samplerState, unsigned long startIndex,
                                  unsigned long count);
    void setFragmentTexture(WebGPUTexture texture, unsigned long index);
    void setFragmentTextures(WebGPUTexture[] textures, unsigned long startIndex,
                             unsigned long count);

    // -- Drawing

    void drawPrimitives(unsigned long type, unsigned long start, unsigned long count);
    // @@ add drawInstanced and drawIndexed

};

interface WebGPUBlitCommandEncoder : WebGPUCommandEncoder {
    // -- Copying Data Between Two Buffers
    void copyFromBufferToBuffer(WebGPUBuffer source, unsigned long sourceOffset,
                                WebGPUBuffer destination,
                                unsigned long destinationOffset, unsigned long size);

    // -- Copying Data From a Buffer to a Texture
    void copyFromBufferToTexture(WebGPUBuffer buffer, unsigned long sourceOffset,
                                 unsigned long sourceBytesPerRow,
                                 unsigned long sourceBytesPerImage, WebGPUSize size,
                                 WebGPUTexture texture,
                                 unsigned long destinationSlice, unsigned long destinationLevel,
                                 WebGPUOrigin origin);

    // -- Copying Data Between Two Textures
    void copyFromTextureToTexture();

    // -- Copying Data from a Texture to a Buffer
    void copyFromTextureToBuffer();
    
    // - Image Operations
    void fillBuffer(WebGPUBuffer buffer, unsigned long start,
                    unsigned long count, unsigned long value);
    void generateMipmapsForTexture();
}

interface WebGPUComputeCommandEncoder : WebGPUCommandEncoder {

    // - Specifying the Compute Pipeline State
    void setComputePipelineState(WebGPUComputePipelineState state);
    
    // -- Buffers and Textures
    void setBuffer(WebGPUBuffer buffer, unsigned long offset, unsigned long index);
    void setBuffers(WebGPUBuffer[] buffers, unsigned long[] offsets,
                    unsigned long startIndex, unsigned long count);

    void setBuffer(ArrayBufferView bufferView, unsigned long index);

    void setTexture(WebGPUTexture texture, unsigned long index);
    void setTextures(WebGPUTexture[] textures, unsigned long startIndex, unsigned long count);

    void setSamplerState(WebGPUSamplerState samplerState, unsigned long index);
    void setSamplerStates(WebGPUSamplerState[] samplerStates, unsigned long startIndex,
                          unsigned long count);

    // @@ do we need setThreadgroupMemoryLength?
    
    // -- Executing
    void dispatch(WebGPUSize threadgroupsPerGrid, WebGPUSize threadsPerThreadgroup);
    // @@ dispatchThreadgroupsWithIndirectBuffer?
};

interface WebGPUViewport {
    attribute double originX;
    attribute double originY;
    attribute double width;
    attribute double height;
    attribute double znear;
    attribute double zfar;
};

[ Constructor ]
interface WebGPURenderPipelineDescriptor {

    attribute DOMString label;

    attribute WebGPUFunction vertexFunction;
    attribute WebGPUVertexDescriptor vertexDescriptor;
    attribute WebGPUFunction fragmentFunction;

    attribute unsigned long sampleCount;
    attribute boolean alphaToCoverageEnabled;
    attribute boolean alphaToOneEnabled;
    attribute boolean rasterizationEnabled;

    readonly attribute WebGPURenderPipelineColorAttachmentDescriptor[] colorAttachments;
    attribute WebGPUPixelFormat depthAttachmentPixelFormat;
    attribute WebGPUPixelFormat stencilAttachmentPixelFormat;

    void reset();

};

interface WebGPURenderPipelineState {
    attribute DOMString label;
};

[ Constructor ]
interface WebGPUComputePipelineDescriptor {

    attribute DOMString label;

    attribute WebGPUFunction computeFunction;
    attribute boolean threadGroupSizeIsMultipleOfThreadExecutionWidth;

    void reset();

};

interface WebGPUComputePipelineState {
    attribute unsigned long maxTotalThreadsPerThreadgroup;
    attribute unsigned long threadExecutionWidth;
};

[ Constructor ]
interface WebGPUDepthStencilDescriptor {

    attribute DOMString label;

    attribute WebGPUCompareFunction depthCompareFunction; 
    attribute boolean depthWriteEnabled;
    
    attribute WebGPUStencilDescriptor backFaceStencil;
    attribute WebGPUStencilDescriptor frontFaceStencil;
};

[ Constructor ]
interface WebGPUStencilDescriptor {

    // -- Specifying Stencil Functions and Operations
    attribute WebGPUStencilOperation stencilFailureOperation;
    attribute WebGPUStencilOperation depthFailureOperation;
    attribute WebGPUStencilOperation depthStencilPassOperation;
    attribute WebGPUCompareFunction stencilCompareFunction;

    // -- Specifying Stencil Bit Mask Properties
    attribute unsigned int readMask;
    attribute unsigned int writeMask;
}

interface WebGPUDepthStencilState {
    attribute DOMString label;
};

interface WebGPUSamplerDescriptor {
    attribute WebGPUSamplerAddressMode rAddressMode;
    attribute WebGPUSamplerAddressMode sAddressMode;
    attribute WebGPUSamplerAddressMode tAddressMode;
    attribute WebGPUSamplerMinMagFilter minFilter;
    attribute WebGPUSamplerMinMagFilter magFilter;
    attribute WebGPUSamplerMipFilter mipFilter;
    attribute float lodMinClamp;
    attribute float lodMaxClamp;
    attribute boolean lodAverage;
    attribute unsigned long maxAnisotropy;
    attribute boolean normalizedCoordinates;
    attribute WebGPUCompareFunction compareFunction;
    attribute DOMString label;
}

interface WebGPUSamplerState {
    attribute DOMString label;
};

interface WebGPURenderPassAttachmentDescriptor {
    
    // -- Texture
    attribute WebGPUTexture texture;
    attribute unsigned long level;
    attribute unsigned long slice;
    attribute unsigned long depthPlane;

    // -- Rendering Pass Actions
    attribute WebGPULoadAction loadAction;
    attribute WebGPUStoreAction storeAction;

    // -- Specifying the Texture to Resolve Multisample Data
    attribute WebGPUTexture resolveTexture;
    attribute unsigned long resolveLevel;
    attribute unsigned long resolveSlice;
    attribute unsigned long resolveDepthPlane;
};

interface WebGPURenderPassColorAttachmentDescriptor : WebGPURenderPassAttachmentDescriptor {

    attribute float[] clearColor; // @@ should color be a type?

};

interface WebGPURenderPassDepthAttachmentDescriptor : WebGPURenderPassAttachmentDescriptor {

    attribute double clearDepth;
    attribute WebGPUMultisampleDepthResolveFilter depthResolveFilter;

};

interface WebGPURenderPassStencilAttachmentDescriptor : WebGPURenderPassAttachmentDescriptor {

    attribute unsigned long clearStencil;

};

[ Constructor ]
interface WebGPURenderPassDescriptor {

    readonly attribute WebGPURenderPassColorAttachmentDescriptor[] colorAttachments;
    attribute WebGPURenderPassDepthAttachmentDescriptor depthAttachment;
    attribute WebGPURenderPassStencilAttachmentDescriptor stencilAttachment;

    attribute WebGPUBuffer visibilityResultBuffer;

};

interface WebGPURenderPipelineColorAttachmentDescriptor {

    // -- Pipeline state
    attribute WebGPUPixelFormat pixelFormat;
    attribute WebGPUColorWriteMask writeMask;

    // -- Blending
    attribute boolean blendingEnabled;
    attribute WebGPUBlendOperation rgbBlendOperation;
    attribute WebGPUBlendOperation alphaBlendOperation;

    // -- Blend Factors
    attribute WebGPUBlendFactor sourceRGBBlendFactor;
    attribute WebGPUBlendFactor destinationRGBBlendFactor;
    attribute WebGPUBlendFactor sourceAlphaBlendFactor;
    attribute WebGPUBlendFactor destinationAlphaBlendFactor;
};

interface WebGPUResource {
    readonly attribute WebGPUCPUCacheMode cpuCacheMode;
    readonly attribute WebGPUStorageMode storageMode;
    readonly attribute DOMString label;
    
    void setPurgeableState(DOMString state);
}

interface WebGPUOrigin {
    attribute unsigned long x;
    attribute unsigned long y;
    attribute unsigned long z;
};

interface WebGPUSize {
    attribute unsigned long width;
    attribute unsigned long height;
    attribute unsigned long depth;
};

interface WebGPURegion {
    attribute WebGPUOrigin origin;
    attribute WebGPUSize size;
}

interface WebGPUTexture : WebGPUResource {

    // @@ need API to provide data from <img>, <canvas>, <video> etc

    // -- Copying Data into a Texture Image
    void replaceRegion(WebGPURegion region, unsigned long mipmapLevel, unsigned long slice,
                       ArrayBufferView bytes, unsigned long bytesPerRow,
                       unsigned long bytesPerImage);
    void replaceRegion(WebGPURegion region, unsigned long mipmapLevel,
                       ArrayBufferView bytes, unsigned long bytesPerRow);

    // -- Copying Data from a Texture Image
    ArrayBufferView getBytes(unsigned long bytesPerRow, unsigned long bytesPerImage,
                             WebGPURegion region, unsigned long mipmapLevel, unsigned long slice);

    // -- Creating Textures by Reusing Image Data
    WebGPUTexture newTextureView(DOMString pixelFormat, DOMString textureType,
                                WebGPURange levelRange, WebGPURange sliceRange);

    // -- Querying Texture Attributes
    readonly attribute DOMString textureType;
    readonly attribute DOMString pixelFormat;
    readonly attribute unsigned long width;
    readonly attribute unsigned long height;
    readonly attribute unsigned long depth;
    readonly attribute unsigned long mipmapLevelCount;
    readonly attribute unsigned long arrayLength;
    readonly attribute unsigned long sampleCount;
    readonly attribute boolean framebufferOnly;
    readonly attribute WebGPUResource rootResource;
    readonly attribute WebGPUTextureUsage usage;

    // -- Querying Parent Texture Attributes
    readonly attribute WebGPUTexture parentTexture;
    readonly attribute unsigned long parentRelativeLevel;
    readonly attribute unsigned long parentRelativeSlice;

    // -- Querying Source Buffer Attributes
    readonly attribute WebGPUBuffer buffer;
    readonly attribute unsigned long bufferOffset;
    readonly attribute unsigned long bufferBytesPerRow;
};

[ Constructor ]
interface WebGPUTextureDescriptor {
    attribute DOMString textureType;
    attribute DOMString pixelFormat;
    attribute unsigned long width;
    attribute unsigned long height;
    attribute unsigned long depth;
    attribute unsigned long mipmapLevelCount;
    attribute unsigned long sampleCount;
    attribute unsigned long arrayCount;
    attribute WebGPUResourceOptions resourceOptions;
    attribute WebGPUCPUCacheMode cpuCacheMode;
    attribute WebGPUStorageMode storageMode;
    attribute WebGPUTextureUsage usage;
};

interface WebGPUBuffer : WebGPUResource {

    WebGPUTexture createTexture(WebGPUTextureDescriptor descriptor,
                               unsigned long offset, unsigned long bytesPerRow);

    readonly attribute unsigned long length;
    readonly attribute ArrayBufferView contents;

};

interface WebGPULibrary {

    readonly attribute DOMString sourceCode;
    attribute DOMString label;
    readonly attribute DOMString[] functionNames;

    WebGPUFunction functionWithName(DOMString name);

};


interface WebGPUFunction {

    readonly attribute DOMString name;
    readonly attribute WebGPUFunctionType functionType;

    readonly attribute WebGPUVertexAttributes[] vertexAttributes;
};

enum WebGPUCompareFunction {
    "never",
    "less",
    "equal",
    "lessequal",
    "greater",
    "notequal",
    "greaterequal",
    "always"
};

enum WebGPUPixelFormat {
    "BGRA8Unorm",
    etc
};

enum WebGPULoadAction {
    "dontcare",
    "load",
    "clear"
};

enum WebGPUStoreAction {
    "dontcare",
    "store",
    "multisampleresolve"
};

enum WebGPUPrimitiveType {
    "point",
    "line",
    "linestrip",
    "triangle",
    "trianglestrip"
};

enum WebGPUFunctionType {
    "fragment",
    "vertex"
};

enum WebGPUStencilOperation {
    "keep",
    "zero",
    "replace",
    "incrementclamp",
    "decrementclamp",
    "invert",
    "incrementwrap",
    "decrementwrap"
};

enum WebGPUStatus {
    "notenqueued",
    "enqueued",
    "committed",
    "scheduled",
    "completed",
    "error"
};

enum WebGPUSamplerAddressMode {
    "clamptoedge",
    "mirrorclamptoedge",
    "repeat",
    "mirrorrepeat",
    "clamptozero"
};

enum WebGPUSamplerMinMagFilter {
    "nearest",
    "linear"
};

enum WebGPUSamplerMipFilter {
    "notmipmapped",
    "nearest",
    "linear"
};

enum WebGPUCullMode {
    "none",
    "front",
    "back"
};

enum WebGPUIndexType {
    "uint16",
    "uint32"
};

enum WebGPUVisibilityResultMode {
    "disabled",
    "boolean",
    "counting"
};

enum WebGPUWinding {
    "clockwise",
    "counterclockwise"
};

enum WebGPUDepthClipMode {
    "clip",
    "clamp"
};

enum WebGPUTriangleFillMode {
    "fill",
    "lines"
};

enum WebGPUCPUCacheMode {
    "defaultcache",
    "writecombined"
};

enum WebGPUStorageMode {
    "shared",
    "managed",
    "private"
};

enum WebGPUResourceOptions {
    "cpucachemodedefaultcache",
    "cpucachemodewritecombined",
    "storagemodeshared",
    "storagemodemanaged",
    "storagemodeprivate",
    "optioncpucachemodedefaultcache",
    "optioncpucachemodewritecombined"
};

enum WebGPUTextureUsage {
    "unknown",
    "shaderread",
    "shaderwrite",
    "rendertarget",
    "pixelformatview"
};

enum WebGPUBlendOperation {
    "add",
    "subtract",
    "reversesubtract",
    "min",
    "max"
};

enum WebGPUBlendFactor {
    "zero",
    "one",
    "sourcecolor",
    "oneminussourcecolor",
    "sourcealpha",
    "oneminussourcealpha",
    "destinationcolor",
    "oneminusdestinationcolor",
    "destinationalpha",
    "oneminusdestinationalpha",
    "sourcealphasaturated",
    "blendcolor",
    "oneminusblendcolor",
    "blendalpha",
    "oneminusblendalpha",
};

enum WebGPUColorWriteMask {
    // This is a mask, so the calling site
    // should take an array of them.
    "none",
    "red",
    "green",
    "blue",
    "alpha",
    "all"
}

enum WebGPUMultisampleDepthResolveFilter {
    "sample0",
    "min",
    "max"
};

enum WebGPUFeatureSet {
    // some names like...
    "level1",
    "level2"
};

4. 着色语言

WebGPU 接受 Metal Shading Language 的一种变体,称为 WebGPU 着色语言。

从标准 Metal 到 WebGPU 语言的高级变化包括

最终,WebGPU 将接受某种 IR 格式的着色器,类似于 DXIL 或 SPIR-V。目前尚不清楚是否会有一种人类可读的着色格式。

示例

示例 1

绘制三角形


let canvas = document.querySelector("canvas");
canvas.width = 500;
canvas.height = 500;
let gpu = canvas.getContext("Webgpu");

let library = gpu.createLibrary(loadShaderFromScript("library"));
library.label = "Example label";

let vertexF = library.functionWithName("vertex_main");
let fragmentF = library.functionWithName("fragment_main");

let vertexData = new Float32Array([
    // x y z 1 r g b 1
    0, 0.75, 0, 1, 1, 0, 0, 1,
    -0.75, -0.75, 0, 1, 0, 1, 0, 1,
    0.75, -0.75, 0, 1, 0, 0, 1, 1
]);
let vertexBuffer = gpu.createBuffer(vertexData);

let pipelineDescriptor = new WebGPURenderPipelineDescriptor();
pipelineDescriptor.vertexFunction = vertexF;
pipelineDescriptor.fragmentFunction = fragmentF;
pipelineDescriptor.colorAttachments[0].pixelFormat = "BGRA8Unorm";

let pipelineState = gpu.createRenderPipelineState(pipelineDescriptor);

let drawable = gpu.nextDrawable();

let passDescriptor = new WebGPURenderPassDescriptor();
passDescriptor.colorAttachments[0].loadAction = "clear";
passDescriptor.colorAttachments[0].storeAction = "store";
passDescriptor.colorAttachments[0].clearColor = [0.8, 0.8, 0.8, 1.0];
passDescriptor.colorAttachments[0].texture = drawable.texture;

let commandQueue = gpu.createCommandQueue();

let commandBuffer = commandQueue.createCommandBuffer();

let commandEncoder = commandBuffer.createRenderCommandEncoder(passDescriptor);
commandEncoder.setRenderPipelineState(pipelineState);
commandEncoder.setVertexBuffer(vertexBuffer, 0, 0);

commandEncoder.drawPrimitives("triangle", 0, 3);
commandEncoder.endEncoding();
commandBuffer.presentDrawable(drawable);
commandBuffer.commit();

示例 2

更新 uniforms


// Setup
let uniformData = new Float32Array([ 0.5 ]);
let uniformBuffer = gpu.createBuffer(uniformData);


// Each frame
let uniformBufferView = new Float32Array(uniformBuffer.contents);
uniformBufferView[0] = frameId * 10; // Some per-frame value

let commandEncoder = commandBuffer.createRenderCommandEncoder(passDescriptor);
commandEncoder.setRenderPipelineState(pipelineState);
commandEncoder.setVertexBuffer(vertexBuffer, 0, 0);
commandEncoder.setVertexBuffer(uniformBuffer, 0, 1);

commandEncoder.drawPrimitives("triangle", 0, 3);
commandEncoder.endEncoding();
commandBuffer.presentDrawable(drawable);
commandBuffer.commit();